/*LICENSE_BEGIN
 * ******************************************************************
 * Copyright 2006 by the Dainamite Robocup-Team at the
 * DAI-Labor, TU-Berlin (www.dai-labor.de)
 * 
 * Members of Dainamite are:
 * Holger Endert
 * Robert Wetzker
 * Thomas Karbe
 * Axel Heßler
 * Philippe Bütter
 * Felix Brossmann
 * 
 * Licensed under the GNU GPL (General Public License) Free Software 
 * License, Version 2. You may use this file only in compliance with 
 * the License. You may obtain a copy of the License at:
 * 
 * http://www.fsf.org/licensing/licenses/gpl.txt
 * ******************************************************************
LICENSE_END*/
/*
 * Created on 18.01.2005
 *
 * A circle.
 *
 */
package robocup.component.geometry;

import java.util.LinkedList;

import robocup.component.worldobjects.Line;

/**
 * A circle.
 */
public class Circle {

	/**
	 * This circle's center.
	 */
	private Vektor _center = new Vektor();

	/**
	 * This circle's radius.
	 */
	private double _radius;

	// these types are reserved for the method:
	// getIntersectionPoints(Circle c): do not use in other contexts
	Vektor tmp = new Vektor();

	Vektor xPoint = new Vektor();

	Line line = new Line(this.xPoint, this.tmp);

	/**
	 * Creates a new circle.
	 * 
	 * @param center
	 *            The center.
	 * @param radius
	 *            The radius.
	 */
	public Circle(Vektor center, double radius) {
		this._center = center.cloned();
		this._radius = radius;
	}

	/**
	 * Creates a new circle by three points Are all points on a line, center and
	 * radius will be set to 0 resp null.
	 * 
	 */
	public Circle(Vektor p1, Vektor p2, Vektor p3) {
		StraightLine p1p2 = new StraightLine(p1, p2);
		if (p1p2.isOnLine(p3)) {
			this._center = null;
			this._radius = 0;
			return;
		}

		StraightLine ms1 = new Line(p1, p3).getPerpendicularBisector();
		StraightLine ms2 = new Line(p2, p3).getPerpendicularBisector();

		this._center = ms1.getIntersectionPoint(ms2);
		this._radius = p1.getDistance(this._center);
	}

	/**
	 * Gets the intersection points to a line.
	 * 
	 * @param line
	 * @return
	 */
	public LinkedList<Vektor> getIntersectionPoints(Line line) {
		return line.getIntersectionPoints(this);
	}

	/**
	 * @param point
	 *            The point.
	 * @return <code>true</code>, if the given point lies within or on the circle,
	 *         <code>false</code> otherwise.
	 */
	public boolean liesWithinCircle(Vektor point) {
		return point.sub(this._center).getLength() <= this._radius;
	}

	/**
	 * This method calculates the intersectionpoints between two circles.
	 * 
	 * @param c -
	 *            the circle for which the intersectionpoints should be
	 *            calculated!
	 * @return a possible empty list of intersectionpoints!
   * 
   * TODO
   * 
   * - test
	 */
	public LinkedList<Vektor> getIntersectionPoints(Circle c) {
		double dist = this.getCenter().getDistance(c.getCenter());
		double r1r2 = this._radius + c._radius;
		// no intersection-points
		if (dist > r1r2)
			return new LinkedList<Vektor>();

		// the length from center of this circle to the
		// point, where the line between the intersectionpoints
		// crosses the line between the two centers of the circle
		double x = ((dist * dist) - (c._radius * c._radius) + (this._radius * this._radius))
				/ (2 * dist);

		// if one circle is completely inside another
		if (x == Double.POSITIVE_INFINITY || x == Double.NEGATIVE_INFINITY)
			return new LinkedList<Vektor>();

		// the resulting point from x
		this.xPoint.x = c._center.x - this._center.x;
		this.xPoint.y = c._center.y - this._center.y;
		this.xPoint.setLength(x);
		this.xPoint.addToThis(this._center);

		// direction to c1
		double dir = this.xPoint.getAngleTo(this._center);
		dir = Vektor.normalize(dir + 90);
		this.tmp.reset();
		this.tmp.x = 0;
		this.tmp.y = 1;
		this.tmp.rotate(dir);
		this.tmp.addToThis(this.xPoint);

		// calculate intersectionpoints from line
		return this.line.getIntersectionPoints(this);
	}

	/**
	 * @return Returns the _center.
	 */
	public Vektor getCenter() {
		return this._center.cloned();
	}

	/**
	 * @param center
	 *            The center to set.
	 */
	public void setCenter(Vektor center) {
		this._center.copy(center);
	}

	/**
	 * @return Returns the radius.
	 */
	public double getRadius() {
		return this._radius;
	}

	/**
	 * @param radius
	 *            The radius to set.
	 */
	public void setRadius(double radius) {
		this._radius = radius;
	}

	/**
	 * @return Returns the circle's diameter.
	 */
	public double getDiameter() {
		return this._radius * 2;
	}

	/**
	 * @return Returns the circle's area.
	 */
	public double getArea() {
		return Math.PI * Math.pow(this._radius, 2);
	}

	/**
	 * @return Returns the circle's circumference.
	 */
	public double getCircumference() {
		return 2 * Math.PI * this._radius;
	}

	/**
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#toString()
	 */
	public String toString() {
		return "center: " + _center + " rad: " + _radius;
	}

}
